﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using CoreEx.Common.Proxy;
using CoreEx.Common.Extensions;
using LinFu.IoC.Configuration;
using LinFu.IoC.Interfaces;
using LinFu.IoC.Configuration;
using Mono.Cecil;
using System.Reflection;

namespace CoreEx.Wcf.Server.Implementation
{
    [Implements(typeof(ITypeModifier),ServiceName = "ServiceProxyTypeModifier")]
    public class ServiceProxyTypeModifier :  ITypeModifier, IInitialize
    {
        private IServiceContainer _serviceContainer;
        public void BeforeConstruction(Type originalBaseType, IEnumerable<Type> baseInterfaces, ModuleDefinition module, TypeDefinition targetType)
        {
            
        }

        public void AfterConstruction(Type originalBaseType, IEnumerable<Type> baseInterfaces, ModuleDefinition module, TypeDefinition targetType)
        {
            Type serviceType = baseInterfaces.Last();
            

            //if (!HasServiceContractAttribute(serviceType))
            //{
                //Import the ServiceContractAttribute 
                MethodReference methodReference = targetType.Module.Import(
                    typeof (ServiceContractAttribute).GetConstructor(new Type[] {}));

                //Add the attribute to the created proxy type
                var customAttribute = new CustomAttribute(methodReference);

                targetType.CustomAttributes.Add(customAttribute);
            //}


            MethodReference operationContractConstructor = targetType.Module.Import(
                    typeof(OperationContractAttribute).GetConstructor(new Type[] { }));

            var serviceMethods = serviceType.GetMethods();
            var targetMethods = targetType.Methods.Cast<MethodDefinition>()
                .Join(serviceMethods,methodDefinitiion => methodDefinitiion.Name,methodInfo
                    => string.Format("{0}.{1}", methodInfo.DeclaringType, methodInfo.Name), 
                    (methodDefinition, methodInfo) => new { methodDefinition, methodInfo });

            foreach (var targetMethod in targetMethods)
            {
                //if (!HasOperationContractContractAttribute(targetMethod.methodInfo))
               // {
                    TypeReference stringType = module.Import(typeof(string));
                    var operationContractAttribute = new CustomAttribute(operationContractConstructor);
                    operationContractAttribute.SetPropertyType("Name", stringType);
                    operationContractAttribute.Properties.Add("Name", string.Format("{0}.{1}", serviceType.FullName, targetMethod.methodInfo.Name));

                    operationContractAttribute.SetPropertyType("Action", stringType);
                    operationContractAttribute.Properties.Add("Action", string.Format("{0}.{1}", serviceType.FullName, targetMethod.methodInfo.Name));
                    operationContractAttribute.SetPropertyType("ReplyAction", stringType);
                    operationContractAttribute.Properties.Add("ReplyAction", string.Format("{0}.ResponseTo{1}", serviceType.FullName, targetMethod.methodInfo.Name));
                    targetMethod.methodDefinition.CustomAttributes.Add(operationContractAttribute);
                //}
            }
            


        }


        private static bool HasServiceContractAttribute(Type type)
        {
            return (type.GetCustomAttributes(typeof (ServiceContractAttribute),true).Count() == 1);
        }

        private static bool HasOperationContractContractAttribute(MethodInfo method)
        {
            return (method.GetCustomAttributes(typeof(OperationContractAttribute), true).Count() == 1);
        }

        public void Initialize(IServiceContainer source)
        {
            _serviceContainer = source;
        }
    }
}
